package pri.damai.xiaowu.auth.security.handle;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import pri.damai.xiaowu.auth.enums.ResultEnum;
import pri.damai.xiaowu.auth.security.model.LogOutData;
import pri.damai.xiaowu.auth.utils.HttpUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.impl.DefaultClaims;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.stereotype.Component;
import pri.damai.xiaowu.common.core.vo.ResultVO;
import pri.damai.xiaowu.common.security.config.SecurityUtil;
import pri.damai.xiaowu.common.security.config.XiaoWuSecurityProperties;
import pri.damai.xiaowu.common.security.constant.AuthConstant;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 登出处理器
 * @Desc
 * @Author DaMai
 * @Date 2021/11/5 11:11
 * 但行好事，莫问前程。
 */
@Slf4j
@Component
public class RuiRuanLogoutHandler implements LogoutHandler {

    @Resource
    RedisTemplate<String, Object> redisTemplate;
    @Resource
    SecurityUtil securityUtil;
    @Resource
    XiaoWuSecurityProperties securityProperties;


    @SneakyThrows
    @Override
    public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {

        String contentType = request.getContentType();

        String token = getToken(request);

        // 获取 token 负载
        Claims claims = new DefaultClaims();
        try {
            claims = securityUtil.getClaims(token);
        } catch (JwtException e) {
            // 处理返回值
            ResultVO<Object> resultVO = ResultVO.error(ResultEnum.INVALID_TOKEN);
            HttpUtil.respJson(response, resultVO);
        }

        // 获取用户 Id
        String userId = claims.get(AuthConstant.USER_ID_KEY, String.class);

        // 删除 Token
        this.deleteToken(userId);

        log.debug("注销用户成功:{}", userId);
    }

    /**
     * 删除 Token
     * @apiNote [附加描述]
     * @param userId 用户Id
     * @return com.ruiruan.pre.common.core.vo.ResultVO<java.lang.Void>
     * @author DaMai
     * @date 2021/10/29 10:47
     */
    private void deleteToken(String userId) {

        String tokenRedisKey = getTokenRedisKey(userId);

        redisTemplate.delete(tokenRedisKey);

    }

    /**
     * 获取用户 Token 的 RedisKey
     * @apiNote [附加描述]
     * @param userId 用户Id
     * @return java.lang.String
     * @author DaMai
     * @date 2021/10/29 10:48
     */
    private String getTokenRedisKey(String userId) {
        return securityProperties.getTokenRedisKey() + userId;
    }

    /**
     * 获取 Token
     * @apiNote [附加描述]
     * @param request 请求
     * @return java.lang.String
     * @author DaMai
     * @date 2021/11/5 12:23
     */
    private String getToken(HttpServletRequest request) throws AuthenticationServiceException {

        HttpUtil.checkContentType(request, HttpMethod.POST, MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_JSON_UTF8_VALUE);

        LogOutData logOutData = new LogOutData();
        ObjectMapper objectMapper = new ObjectMapper();
        // 未知字段不报错
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        try {
            logOutData = objectMapper.readValue(request.getInputStream(), LogOutData.class);
        } catch (IOException e) {
            log.error("获取注销参数失败。", e);
            throw new AuthenticationServiceException("获取注销参数失败。" + e.getMessage());
        }

        return logOutData.getToken();
    }

}
